#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

const uint32_t gcap = 5;

template <typename T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _bq.size() >= _cap;
    }

    bool IsEmpty()
    {
        return _bq.empty();
    }

public:
    BlockQueue(uint32_t cap = gcap) : _cap(cap), _c_cond_num(0), _p_cond_num(0)
    {}

    void Push(const T &in)
    {
        {
            LockGuard Lock(&_lock);
            while (IsFull()) // 队列满了
            {
                _p_cond_num++;
                _p_cond.Wait(_lock);
                _p_cond_num--;
            }
            // 队列有空余
            _bq.push(in); // 完成生产
            // 唤醒消费者,这一步在放在解锁前后都可以
            if (_c_cond_num > 0)
                _c_cond.NotifyOne();
        }
        //Other code
    }

    void Pop(T *out)
    {
        {
            LockGuard Lock(&_lock);
            while (IsEmpty()) // 队列为空
            {
                _c_cond_num++;
                _c_cond.Wait(_lock);
                _c_cond_num--;
            }
            // 队列不为空
            *out = _bq.front();
            _bq.pop(); // 完成消费
            // 唤醒生产者,这一步在放在解锁前后都可以
            if (_p_cond_num > 0)
                _p_cond.NotifyOne();
        }
        //Other code
    }

    ~BlockQueue()
    {}

private:
    std::queue<T> _bq;
    uint32_t _cap; // 队列容量

    Mutex _lock;  // 互斥锁
    Cond _c_cond; // 消费者条件变量
    Cond _p_cond; // 生产者条件变量

    int _c_cond_num; // 正在阻塞的消费者
    int _p_cond_num; // 正在阻塞的生产者
};